#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
//交易场所
template <class T>
class BlockQueue
{
public:
     bool isFull()  {return _q.size() == _cap;}
     bool isEmpty() {return _q.empty();}
    // 构造
    BlockQueue(int num = 5):_cap(num)
    {
        //对锁和条件变量进行初始化
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&Productorcond,nullptr);
        pthread_cond_init(&Consumercond,nullptr);
    }
    //压入数据-->生产
    void push(T in)
    {
        //为了防止生产者在生产的过程中，消费者进行消费，需要对临界资源进行加锁
        pthread_mutex_lock(&_mutex);
        //生产之前，需要判断队列是否满了，满了则进行等待
        // if(isFull())
       while(isFull())  //为了hold住多生产者
        {
           pthread_cond_wait(&Productorcond,&_mutex); //线程进行休眠(切换)等待时，需要将持有的锁进行释放
           //当线程被唤醒之后，在pthread_cond_wait出来前，又要重新申请锁，直到申请成功才会彻底返回，从休眠的地方继续向后运行
        }
        //未满，生产
         _q.push(in);
         //加策略：生产者唤醒消费者
         //if(_q.size() > _cap/2)
        pthread_cond_signal(&Consumercond);
        pthread_mutex_unlock(&_mutex);
    }
    //弹出数据-->消费
    void pop(T *out)
    {
        pthread_mutex_lock(&_mutex);
        //消费之前，需要判断队列是否为空
        //if(isEmpty())
        while(isEmpty())
        {
            pthread_cond_wait(&Consumercond,&_mutex);
        }
         *out = _q.front();
         _q.pop();
         //加策略：消费者唤醒生产者
        pthread_cond_signal(&Productorcond);
        pthread_mutex_unlock(&_mutex);
    }
    // 析构
    ~BlockQueue()
    {
       //对锁和条件变量进行销毁
       pthread_mutex_destroy(&_mutex);
       pthread_cond_destroy(&Productorcond);
       pthread_cond_destroy(&Consumercond);
    }

private:
    std::queue<T> _q;           // 缓冲区
    int _cap;                // 缓冲区的大小
    pthread_mutex_t _mutex; // 消费者和生产者访问同一块空间需要加锁,一个对象一把锁，即消费者和生产者使用的是同一把锁
    //pthread_cond_t *_cond;   // 加入条件变量，防止饥饿问题
    //为了解决消费者和生产者通信问题，当消费者阻塞时需要生产者将其唤醒，当生产者阻塞时需要消费者将其唤醒，
    //所以需要两个条件变量
    pthread_cond_t Productorcond;  //生产者信号量，队列为满就等待
    pthread_cond_t Consumercond; //消费者信号量，队列为空就等待
};